#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
cron: 0 6 * * *
new Env('标准网铜钱签到')
------------------------------------------
功能：标准网签到，领取铜钱，支持自动识别并切换用户组
变量：BZCOM_COOKIE  （多账号新建多个环境变量）
日期：2025-09-18
网站：https://bbs.biaozhuns.com/
特性：
    1. 自动识别所有可切换的用户组（包括新增组）
    2. 智能提取切换链接并自动补全完整URL
    3. 按配置优先级自动选择切换目标
    4. 详细日志输出，便于调试
"""
import os
import re
import html
import sys
import time
import traceback
import requests

# ---------- 聚合推送配置 ----------
AGGRE_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dailylog')

IMMEDIATE = False          # True 实时推送，False 汇总推送
def push(title, text=""):
    if IMMEDIATE:
        from notify import send
        send(title, text)
    else:
        with open(AGGRE_FILE, "a", encoding="utf-8") as f:
            f.write(f"{title}\n{text}\n{'-'*40}\n")

from bs4 import BeautifulSoup
# 测试用环境变量
# os.environ['BZCOM_COOKIE'] = ''

# ---------- 核心配置：用户组优先级管理 ----------
# 配置说明：仅需指定已知组的优先级，新增组会自动识别并按默认优先级先1再11处理
USER_GROUP_PRIORITY = {
    10: {"name": "预备役", "priority": 1},      # 优先级1
    25: {"name": "上等兵", "priority": 0},      # 优先级2，由于我现在是这个，我先改成0
    11: {"name": "少尉", "priority": 3},        # 优先级3
    12: {"name": "中尉", "priority": 4},        # 优先级4
    13: {"name": "上尉", "priority": 5},        # 优先级5
    14: {"name": "少校", "priority": 6},        # 优先级6
    21: {"name": "中校", "priority": 7},        # 优先级7
    15: {"name": "上校", "priority": 8},        # 优先级8
    22: {"name": "少将", "priority": 9},        # 优先级9
    23: {"name": "中将", "priority": 10},       # 优先级10
    24: {"name": "上将", "priority": 11},       # 优先级11
}
DEFAULT_GROUP_PRIORITY = 99  # 未配置组的默认优先级

# ---------- 固定配置 ----------
BASE_DOMAIN = "https://bbs.biaozhuns.com"
GROUP_LIST_URL = f"{BASE_DOMAIN}/home.php?mod=spacecp&ac=usergroup&do=list"
GET_HASH_URL = (
    f"{BASE_DOMAIN}/plugin.php"
    "?id=wq_sign&mod=mood&infloat=yes&handlekey=pc_click_wqsign"
    "&inajax=1&ajaxtarget=fwin_content_pc_click_wqsign"
)
SIGN_URL = (
    f"{BASE_DOMAIN}/plugin.php"
    "?id=wq_sign&mod=mood&infloat=yes&confirmsubmit=yes&inajax=1"
)
SIGN_STATUS_URL = f"{BASE_DOMAIN}/plugin.php?id=wq_sign:wq_sign"

UA = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
    "(KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0"
)

BASE_HEADERS = {
    "user-agent": UA,
    "accept": "*/*",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "referer": f"{BASE_DOMAIN}/plugin.php?id=wq_sign:wq_sign",
    "sec-ch-ua": '"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"Windows"',
    "x-requested-with": "XMLHttpRequest",
}

# ---------- 工具函数：URL处理 ----------
def complete_url(relative_url: str) -> str:
    """将相对路径转换为完整URL"""
    if not relative_url:
        return ""
    if relative_url.startswith(("http://", "https://")):
        return relative_url
    if relative_url.startswith("/"):
        return f"{BASE_DOMAIN}{relative_url}"
    return f"{BASE_DOMAIN}/{relative_url}"

# ---------- 核心函数：用户组页面解析 ----------
def parse_user_group_page(html_content: str) -> dict:
    """
    解析用户组列表页面HTML，提取结构化信息
    支持自动识别新用户组，无需手动配置
    """
    parse_result = {
        "current_group": "未提取到",
        "available_groups": [],  # 包含所有可切换组的信息
        "parse_success": False
    }
    
    try:
        # 1. 初始化解析器
        soup = BeautifulSoup(html_content, 'html.parser')
        print("✅ 1. 页面HTML解析器初始化成功")

        # 2. 提取当前用户组
        current_group = get_current_group(html_content)
        parse_result["current_group"] = current_group
        print(f"✅ 2. 提取当前用户组：{current_group}")

        # 3. 定位切换链接（精确选择器优先）
        # 优先使用精确选择器：第5列的切换链接
        switch_links = soup.select('#ct > div.mn > div > table > tbody:nth-child(2) > tr > td:nth-child(5) > a')
        
        # 方式2：通用选择器（匹配包含特定参数的链接）
        if not switch_links:
            print("⚠️  精确选择器定位失败，尝试通用选择器")
            switch_links = soup.select('a[href*="mod=spacecp"][href*="ac=usergroup"][href*="do=switch"][href*="groupid="]')
        
        # 方式3：正则匹配方案（最后兜底）
        if not switch_links:
            print("⚠️  通用选择器定位失败，尝试正则匹配")
            switch_links = soup.find_all('a', href=re.compile(r'groupid=\d+&do=switch'))

        if not switch_links:
            print("❌ 未找到任何切换链接")
            return parse_result

        print(f"✅ 3. 找到 {len(switch_links)} 个切换链接")

        # 4. 提取每个链接的详细信息
        for idx, link in enumerate(switch_links, 1):
            try:
                # 4.1 提取用户组ID
                group_id_match = re.search(r'groupid=(\d+)', link['href'])
                if not group_id_match:
                    print(f"⚠️  第{idx}个链接未找到groupid，跳过")
                    continue
                group_id = int(group_id_match.group(1))

                # 4.2 提取用户组名称（从所在行的第一个单元格）
                row = link.find_parent('tr')
                if not row:
                    print(f"⚠️  第{idx}个链接未找到所在行，跳过")
                    continue
                    
                group_name_td = row.find('td')  # 第一个单元格
                if not group_name_td:
                    print(f"⚠️  第{idx}行未找到组名单元格，跳过")
                    continue
                    
                group_name = group_name_td.get_text(strip=True)
                if not group_name:
                    print(f"⚠️  第{idx}行组名为空，跳过")
                    continue

                # 4.3 补全切换链接为完整URL
                switch_url = complete_url(link['href'])

                # 4.4 确定优先级（配置过的用配置值，否则用默认值）
                priority = USER_GROUP_PRIORITY.get(
                    group_id, 
                    {"priority": DEFAULT_GROUP_PRIORITY}
                )["priority"]

                # 4.5 添加到可用组列表
                parse_result["available_groups"].append({
                    "group_id": group_id,
                    "group_name": group_name,
                    "switch_url": switch_url,
                    "priority": priority  # 记录优先级用于排序
                })
                print(f"   第{idx}个：ID={group_id}，名称={group_name}，优先级={priority}，链接={switch_url[:50]}...")

            except Exception as e:
                print(f"⚠️  解析第{idx}个链接失败：{str(e)[:50]}，跳过")
                continue

        # 5. 按优先级排序可用组（数字越小优先级越高）
        parse_result["available_groups"].sort(key=lambda x: x["priority"])
        
        # 6. 标记解析成功
        parse_result["parse_success"] = True
        print(f"✅ 4. 成功提取 {len(parse_result['available_groups'])} 个可用切换组（已按优先级排序）")

    except Exception as e:
        print(f"❌ 解析用户组页面整体失败：{str(e)}")

    return parse_result

# ---------- 其他工具函数 ----------
def get_current_group(html_text: str) -> str:
    """返回当前用户组名称"""
    m = re.search(r"当前用户组:\s*([^<>\s]+)", html_text)
    return m.group(1).strip() if m else ""

def extract_formhash(resp_text: str) -> str:
    """从响应中提取formhash"""
    cdata = re.search(r"<!\[CDATA\[(.*?)]]>", resp_text, flags=re.S)
    html_snippet = cdata.group(1) if cdata else resp_text
    m1 = re.search(r'<input[^>]*name=["\']formhash["\'][^>]*value=["\']([^"\']+)["\']', html_snippet)
    if m1:
        return m1.group(1)
    m2 = re.search(r'<input[^>]*value=["\']([^"\']+)["\'][^>]*name=["\']formhash["\']', html_snippet)
    return m2.group(1) if m2 else ""

def switch_to_group(session: requests.Session, group_info: dict) -> tuple[bool, str]:
    """
    切换到指定用户组
    :param group_info: 包含group_id, group_name, switch_url的字典
    :return: (是否成功, 结果消息)
    """
    group_id = group_info["group_id"]
    group_name = group_info["group_name"]
    switch_url = group_info["switch_url"]
    
    try:
        # 1. 获取切换弹窗页面，提取formhash
        print(f"📥 正在获取{group_name}切换弹窗...")
        r1 = session.get(switch_url, timeout=15)
        r1.raise_for_status()
        
        formhash = extract_formhash(r1.text)
        if not formhash:
            return False, f"{group_name}切换弹窗中未找到formhash"

        # 2. 构造提交URL（从切换链接中提取基础URL）
        submit_url_match = re.match(r'(.*?)\?', switch_url)
        if not submit_url_match:
            return False, f"无法解析{group_name}的提交URL"
        
        submit_url = f"{submit_url_match.group(1)}?mod=spacecp&ac=usergroup&do=switch&groupid={group_id}&inajax=1"

        # 3. 提交切换请求
        data = {
            "formhash": formhash,
            "groupsubmit": "true",
            "referer": GROUP_LIST_URL,
            "handlekey": "group",
            "gid": ""
        }

        post_headers = BASE_HEADERS.copy()
        post_headers.update({
            "content-type": "application/x-www-form-urlencoded",
            "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "sec-fetch-dest": "iframe",
            "sec-fetch-mode": "navigate",
            "upgrade-insecure-requests": "1",
            "referer": GROUP_LIST_URL
        })

        print(f"📤 正在提交{group_name}切换请求...")
        r2 = session.post(submit_url, data=data, headers=post_headers, timeout=15)
        r2.raise_for_status()

        # 4. 验证切换结果
        success_flag = f"您已切换到用户组 {group_name}"
        if success_flag in r2.text:
            return True, f"成功切换到{group_name}"
        else:
            return False, f"{group_name}切换请求成功，但未检测到成功标识"

    except Exception as e:
        return False, f"{group_name}切换失败：{str(e)[:50]}"

def auto_switch_best_group(session: requests.Session, available_groups: list) -> tuple[bool, str]:
    """自动选择优先级最高的可用组进行切换"""
    if not available_groups:
        return False, "没有可用的切换组"

    # 按优先级依次尝试切换
    for group in available_groups:
        group_name = group["group_name"]
        print(f"\n🔄 尝试切换到{group_name}（优先级：{group['priority']}）")
        success, msg = switch_to_group(session, group)
        if success:
            return True, msg
        else:
            print(f"❌ {msg}，尝试下一个组...")

    return False, f"所有可用组（{[g['group_name'] for g in available_groups]}）均切换失败"

def extract_tip(resp_text: str) -> str:
    """提取签到结果提示语"""
    cdata = re.search(r"<!\[CDATA\[(.*?)]]>", resp_text, flags=re.S)
    if not cdata:
        return ""
    js = cdata.group(1)
    m = re.search(r"showDialog\('([^']+?)'\s*,\s*'notice'", js)
    if m:
        return html.unescape(m.group(1))
    m = re.search(r"succeedhandle_\w+\([^,]+,\s*'([^']+)'", js)
    return html.unescape(m.group(1)) if m else ""

def extract_sign_status(html_content: str) -> tuple:
    """提取签到天数和铜钱数"""
    soup = BeautifulSoup(html_content, 'html.parser')
    
    days_span = soup.find('span', class_='wqpc_red', string=lambda t: t and t.strip().isdigit())
    days = days_span.get_text() if days_span else "未找到"
    
    coins_span = soup.find('span', class_='wqpc_red', string=lambda t: t and '铜钱' in t)
    coins = coins_span.get_text() if coins_span else "未找到"
    
    return days, coins

# ---------- 核心签到逻辑 ----------
def sign_one_cookie(cookie: str) -> str:
    cookie = cookie.strip()
    headers = BASE_HEADERS.copy()
    headers["cookie"] = cookie

    session = requests.Session()
    session.headers.update(headers)
    xi_yb = ""  # 用于记录用户组处理结果
    
    try:
        # 1. 解析用户组列表页面
        print("\n" + "="*50)
        print("开始解析用户组列表页面")
        print("="*50)

        # 1.1 获取用户组列表页面
        print("\n📡 1. 发送请求获取用户组列表页面...")
        g_resp = session.get(GROUP_LIST_URL, timeout=15)
        g_resp.raise_for_status()
        print(f"✅ 请求成功，状态码：{g_resp.status_code}，文本长度：{len(g_resp.text)}字符")

        # 1.2 解析页面内容
        print("\n🔍 2. 解析用户组信息...")
        group_info = parse_user_group_page(g_resp.text)

        # 1.3 处理用户组切换
        print("\n⚙️  3. 处理用户组切换...")
        cur_group = group_info["current_group"]
        available_groups = group_info["available_groups"]

        # 打印解析结果
        print(f"\n📊 解析结果汇总：")
        print(f"   当前用户组：{cur_group}")
        print(f"   可用切换组：{[g['group_name'] for g in available_groups]}")
        print(f"   解析状态：{'成功' if group_info['parse_success'] else '失败'}")

        # 1.4 决定是否需要切换
        if cur_group == "认证会员":
            print(f"\n⚠️  检测到当前为{cur_group}，需要切换用户组")
            
            if not available_groups:
                xi_yb = "检测到当前为认证会员，但未找到任何可切换的用户组，无法签到"
            else:
                # 自动切换到优先级最高的可用组
                switch_success, switch_msg = auto_switch_best_group(session, available_groups)
                xi_yb = f"认证会员自动切换结果：{switch_msg}"
                if switch_success:
                    time.sleep(5)  # 给服务器缓冲时间
        else:
            xi_yb = f"当前用户组：{cur_group}，无需切换"

    except Exception as e:
        xi_yb = f"[警告] 用户组处理异常：{str(e)}"

    print(f"\n🎯 用户组处理结果：\n{xi_yb}")
    print("="*50 + "\n")

    # 2. 执行签到流程
    try:
        # 2.1 检查签到状态
        print("📋 检查签到状态...")
        status_resp = session.get(SIGN_STATUS_URL, timeout=15)
        status_resp.raise_for_status()
        status_html = status_resp.text

        # 2.2 获取签到所需的formhash
        print("🔑 获取签到formhash...")
        hash_resp = session.get(GET_HASH_URL, timeout=15)
        hash_resp.raise_for_status()
        
        if "今日已签到" in hash_resp.text:
            tip = "今日已签到，无需重复操作"
        else:
            formhash = extract_formhash(hash_resp.text)
            if not formhash:
                return f"{xi_yb}\n签到失败：未获取到formhash，可能Cookie已失效"

            # 2.3 提交签到请求
            print("📤 提交签到请求...")
            sign_data = {
                "confirmsubmit": "yes",
                "formhash": formhash,
                "handlekey": "pc_click_wqsign",
                "imageurl": "source/plugin/wq_sign/static/images/wq_sign3.png",
                "message": "宝宝心里委屈，但宝宝不说",
            }

            sign_headers = BASE_HEADERS.copy()
            sign_headers.update({
                "content-type": "application/x-www-form-urlencoded",
                "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                "sec-fetch-dest": "iframe",
                "sec-fetch-mode": "navigate",
                "upgrade-insecure-requests": "1",
            })

            sign_resp = session.post(SIGN_URL, data=sign_data, headers=sign_headers, timeout=15)
            sign_resp.raise_for_status()
            tip = extract_tip(sign_resp.text) or "签到成功，未获取到具体提示"

        # 2.4 获取签到状态详情
        days, coins = extract_sign_status(status_html)
        return f"{xi_yb}\n{tip}\n当前状态：连续签到{days}天，累计获得{coins}"

    except Exception as e:
        return f"{xi_yb}\n签到过程异常：{str(e)}"

# ---------- 主程序 ----------
def main():
    # 获取Cookie
    cookies = os.getenv("BZCOM_COOKIE")
    if not cookies:
        print("未检测到环境变量 BZCOM_COOKIE")
        sys.exit(1)

    cookies = [c for c in re.split(r"[&\n]", cookies) if c.strip()]
    if not cookies:
        print("错误：未提供任何有效的Cookie")
        sys.exit(1)

    all_messages = [f"标准网签到任务开始（共 {len(cookies)} 个账号）"]
    
    for idx, ck in enumerate(cookies, 1):
        print(f"\n\n===== 处理账号 {idx}/{len(cookies)} =====")
        try:
            result = sign_one_cookie(ck)
            all_messages.append(f"===== 账号{idx}结果 =====\n{result}")
            if idx < len(cookies):
                time.sleep(10)  # 账号间间隔，避免请求过于频繁
        except Exception:
            error_msg = f"账号{idx}处理异常：{traceback.format_exc()}"
            all_messages.append(error_msg)
            print(error_msg)
        time.sleep(2)

    # 发送通知
    if all_messages:
        combined_msg = "\n\n".join(all_messages)
        push("标准网签到汇总", combined_msg)
        print(f"\n通知已发送：\n{combined_msg}")

    print("\n所有账号处理完毕")

if __name__ == "__main__":
    main()
    